package com.acme.bankapp.network;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.StringTokenizer;

import com.acme.bankapp.domain.Bank;
import com.acme.bankapp.domain.Client;
import com.acme.bankapp.exceptions.ClientExistsException;
import com.acme.bankapp.service.AccountService;
import com.acme.bankapp.service.BankIOService;
import com.acme.bankapp.service.ClientService;

public class BankServer implements Runnable {
	//	ServerSocket providerSocket;
	//	Socket connection = null;
	//	ObjectOutputStream out;
	//	ObjectInputStream in;
	String message;

	Bank bank;
	ClientService clientService;
	AccountService accountService;
	BankIOService bIOService;

	public BankServer(Bank bank, ClientService clientService, AccountService accountService, BankIOService bIOService) {
		this.bank = bank;
		this.clientService = clientService;
		this.accountService = accountService;
		this.bIOService = bIOService;
	}

	/**
	 * Listens for incoming connections.
	 * Initializes input/output streams (Object...Stream).
	 * Handles communication.
	 */
	public void run() {
		try (ServerSocket providerSocket = new ServerSocket(2004, 10);) { 
			while (true) {
				// 1. creating a server socket
				// 2. Wait for connection
				System.out.println("[Server] " +"Waiting for connection");
				Socket connection = providerSocket.accept();
				System.out.println("[Server] " +"Connection received from "
						+ connection.getInetAddress().getHostName());

				// 3. get Input and Output streams
				try (   ObjectOutputStream out = new ObjectOutputStream(connection.getOutputStream());
						ObjectInputStream  in = new ObjectInputStream(connection.getInputStream())  ) {
					out.flush();
					sendMessage("Connection successful", out);

					communicate(in, out);

				} catch (IOException e) {
					if (e.getClass() == SocketException.class) {
						System.out.println("--- [Server] Socket appears to be closed...");
					} else {
						e.printStackTrace();
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Handles communication.
	 * @param out ObjectOutputStream
	 * @param in ObjectInputStream
	 * @throws IOException
	 */
	void communicate(ObjectInputStream in, ObjectOutputStream out)
			throws IOException {
		String command = "";
		// 4. The two parts communicate via the input and output streams
		do {
			try {
				command = (String) in.readObject();
				message = processCommand(command);
				System.out.println("[Server] " +"client>" + command);
				sendMessage(message, out);
				if ("bye".equals(message)) {
					sendMessage("bye", out);
				}
			} catch (ClassNotFoundException classnot) {
				System.err.println("Data received in unknown format");
			} catch (EOFException eof) {
				System.out.println("Disconnected.");
				message = "bye";
			}
		} while (!"bye".equals(message));
	}


	/**
	 * Parses command from client and executes it.
	 * @param readObject
	 * @return
	 */
	private String processCommand(String command) {
		if (command == null) {
			return "Command should not be null!";
		} else if ("bye".equals(command)) {
			return "bye";
		} else if ("printAllClients".equals(command)) {
			return "\n" + BankIOService.getAllAccountsString(bank);
		} else if (command.indexOf("addClient") == 0) {
			Client client = bIOService.createClientFromStringTokenizer(new StringTokenizer(command.substring(9)), ";");
			try {
				bank.addClient(client);
				return "\nClient " + client + " has been added.";
			} catch (ClientExistsException e) {
				return "\nClient " + client + " has NOT been added - already exists.";
			}
		} else {
			return "Command \"" + command + "\" unknown.";
		}
	}

	void sendMessage(final Object msg, ObjectOutputStream out) {
		try {
			out.writeObject(msg);
			out.flush();
			System.out.println("[Server] " +"server>" + msg);
		} catch (IOException ioException) {
			System.out.println("--- [Server] Output error (connection may be lost or terminated).");
		}
	}

}
